/**
 * Created by IntelliJ IDEA.
 * User: martlenn
 * Date: 23-Jan-2008
 * Time: 08:19:34
 */
package org.hkupp.gui;

import be.proteomics.util.interfaces.Connectable;
import be.proteomics.util.gui.FlamableJFrame;
import org.hkupp.db.accessors.*;
import org.hkupp.gui.dialogs.ConnectionDialog;
import org.hkupp.gui.progressbar.DefaultProgressBar;
import org.hkupp.io.TagSummaryReader;
import org.hkupp.model.IdentifiedRun;
import org.hkupp.util.SeparatedStringSplitter;
import org.hkupp.util.workers.StoreDataWorker;
import org.hkupp.util.workers.TagSummaryFileFinderWorker;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.File;
import java.io.IOException;
import java.io.FilenameFilter;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.ArrayList;
/*
 * CVS information:
 *
 * $Revision$
 * $Date$
 */

/**
 * This class reads a folder with Spectrum Mill output folders per run,
 * imports all the retrieved information, and stores it in the HKUPP
 * version 2 database.
 *
 * @author Lennart Martens
 * @version $Id$
 */
public class SpectrumMillDataImporter extends FlamableJFrame implements Connectable {

    private JComboBox cmbExperiment = null;
    private JComboBox cmbInstrument = null;
    private JTextField txtInputFolder = null;


    private String iBaseTitle = null;
    private Connection iConn = null;
    private String iDBName = null;

    /**
     * This constructor will intialize the GUI, after prompting the user for the
     * database connection using the standard connection dialog copied from ms_lims.
     *
     * @param aTitle    String with the title for the frame.
     */
    public SpectrumMillDataImporter(String aTitle) {
        super(aTitle);
        this.iBaseTitle = aTitle;
        this.addWindowListener(new WindowAdapter() {
            /**
             * Invoked when a window is in the process of being closed.
             * The close operation can be overridden at this point.
             */
            public void windowClosing(WindowEvent e) {
                SpectrumMillDataImporter.this.setVisible(false);
                SpectrumMillDataImporter.this.close(0);
            }
        });
        // Get database connection.
        ConnectionDialog cd = new ConnectionDialog(this, this, "Connection for Spectrum Mill data importer", "SpectrumMillDataImporter.properties");
        cd.setVisible(true);
        if(iConn == null) {
            close(0);
        }

        // Get data for instruments.
        Instrument[] instruments = this.getInstruments();
        Experiment[] experiments = this.getExperiments();

        // Set up GUI.
        cmbExperiment = new JComboBox(experiments);
        cmbExperiment.setMaximumSize(new Dimension(cmbExperiment.getMaximumSize().width, cmbExperiment.getPreferredSize().height));
        JPanel jpanExperiment = new JPanel();
        jpanExperiment.setLayout(new BoxLayout(jpanExperiment, BoxLayout.X_AXIS));
        jpanExperiment.setBorder(BorderFactory.createTitledBorder("Experiment selection"));
        jpanExperiment.add(Box.createHorizontalStrut(5));
        jpanExperiment.add(cmbExperiment);
        jpanExperiment.add(Box.createHorizontalGlue());

        cmbInstrument = new JComboBox(instruments);
        cmbInstrument.setMaximumSize(new Dimension(cmbInstrument.getMaximumSize().width, cmbInstrument.getPreferredSize().height));
        JPanel jpanInstrument = new JPanel();
        jpanInstrument.setLayout(new BoxLayout(jpanInstrument, BoxLayout.X_AXIS));
        jpanInstrument.setBorder(BorderFactory.createTitledBorder("Instrument selection"));
        jpanInstrument.add(Box.createHorizontalStrut(5));
        jpanInstrument.add(cmbInstrument);
        jpanInstrument.add(Box.createHorizontalGlue());

        txtInputFolder = new JTextField(35);
        txtInputFolder.setEditable(false);
        JButton btnInputFolder = new JButton("Browse...");
        btnInputFolder.setMnemonic(KeyEvent.VK_B);
        btnInputFolder.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                File startHere = new File("/");
                // Open the filechooser on the root or the folder the user
                // already specified (if it exists).
                if(!txtInputFolder.getText().trim().equals("")) {
                    File f= new File(txtInputFolder.getText().trim());
                    if(f.exists()) {
                        startHere = f;
                    }
                }
                JFileChooser jfc = new JFileChooser(startHere);
                int returnVal = 0;
                jfc.setDialogType(JFileChooser.CUSTOM_DIALOG);
                jfc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
                returnVal = jfc.showDialog(SpectrumMillDataImporter.this, "Select folder");
                if(returnVal == JFileChooser.APPROVE_OPTION) {
                    txtInputFolder.setText(jfc.getSelectedFile().getAbsoluteFile().toString());
                }
            }
        });

        JPanel jpanInputFolder = new JPanel();
        jpanInputFolder.setLayout(new BoxLayout(jpanInputFolder, BoxLayout.X_AXIS));
        jpanInputFolder.setBorder(BorderFactory.createTitledBorder("Select input folder"));
        jpanInputFolder.add(Box.createHorizontalStrut(5));
        jpanInputFolder.add(new JLabel("Select input folder: "));
        jpanInputFolder.add(Box.createHorizontalStrut(5));
        jpanInputFolder.add(txtInputFolder);
        jpanInputFolder.add(Box.createHorizontalStrut(5));
        jpanInputFolder.add(btnInputFolder);
        jpanInputFolder.add(Box.createHorizontalGlue());

        JPanel jpanMain = new JPanel();
        jpanMain.setLayout(new BoxLayout(jpanMain, BoxLayout.Y_AXIS));
        jpanMain.add(Box.createVerticalStrut(5));
        jpanMain.add(jpanExperiment);
        jpanMain.add(Box.createVerticalStrut(5));
        jpanMain.add(jpanInstrument);
        jpanMain.add(Box.createVerticalStrut(5));
        jpanMain.add(jpanInputFolder);
        jpanMain.add(Box.createVerticalStrut(10));
        jpanMain.add(this.getButtonPanel());
        jpanMain.add(Box.createVerticalStrut(5));

        this.getContentPane().add(jpanMain, BorderLayout.CENTER);
    }

    /**
     * This method will be called by the class actually making the connection.
     * It will pass the connection and an identifier String for that connection
     * (typically the name of the database connected to).
     *
     * @param aConn   Connection with the DB connection.
     * @param aDBName String with an identifier for the connection, typically the
     *                name of the DB connected to.
     */
    public void passConnection(Connection aConn, String aDBName) {
        if(aConn == null) {
            this.close(0);
        } else {
            iConn = aConn;
            iDBName = aDBName;
            this.setTitle(iBaseTitle + " (connected to " + iDBName + ")");
        }
    }

    public boolean isStandAlone() {
        return true;
    }

    public static void main(String[] args) {
        SpectrumMillDataImporter smdi = new SpectrumMillDataImporter("Spectrum Mill data importer");
        smdi.setLocation(100, 100);
        smdi.pack();
        smdi.setVisible(true);
    }

    private static void printError(String aMessage) {
        System.err.println("\n\n" + aMessage + "\n\n");
        System.exit(1);
    }

    /**
     * This method will close the application and shut down the JVM.
     *
     * @param   aStatus int with the status bit to set.
     */
    private void close(int aStatus) {
        if(iConn != null) {
            try {
                iConn.close();
                System.out.println("\n\nConnection to " + iDBName + " closed.\n\n");
            } catch(SQLException sqle) {
                // Nothing much we can do, so ignore.
            }
        }
        System.exit(aStatus);
    }

    /**
     * This method reads all Experiments from the database.
     *
     * @return  Dbuser[] with all the Experiments.
     */
    private Experiment[] getExperiments() {
        Experiment[] result = null;
        try {
            result = Experiment.getExperiments(iConn);
            if(result == null || result.length == 0) {
                JOptionPane.showMessageDialog(this, new String[]{"No experiments were found in the database!", "Shutting down."}, "No experiments found in the database!", JOptionPane.ERROR_MESSAGE);
                close(1);
            }
        } catch(SQLException sqle) {
            JOptionPane.showMessageDialog(this, new String[]{"Unable to load experiments from database!", "Shutting down.", sqle.getMessage()}, "Unable to load experiments!", JOptionPane.ERROR_MESSAGE);
            close(1);
        }
        return result;
    }

    /**
     * This method reads all instruments from the database.
     *
     * @return  Instrument[] with all the instruments.
     */
    private Instrument[] getInstruments() {
        Instrument[] result = null;
        try {
            result = Instrument.getInstruments(iConn);
            if(result == null || result.length == 0) {
                JOptionPane.showMessageDialog(this, new String[]{"No instruments were found in the database!", "Shutting down."}, "No instruments found in the database!", JOptionPane.ERROR_MESSAGE);
                close(1);
            }
        } catch(SQLException sqle) {
            JOptionPane.showMessageDialog(this, new String[]{"Unable to load instruments from database!", "Shutting down.", sqle.getMessage()}, "Unable to load instruments!", JOptionPane.ERROR_MESSAGE);
            close(1);
        }
        return result;
    }

    private JPanel getButtonPanel() {

        JButton btnStoreData = new JButton("Store data");
        btnStoreData.setMnemonic(KeyEvent.VK_S);
        btnStoreData.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                storeDataTriggered();
            }
        });

        JButton btnExit = new JButton("Exit");
        btnExit.setMnemonic(KeyEvent.VK_X);
        btnExit.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                close(0);
            }
        });

        JPanel jpanButtons = new JPanel();
        jpanButtons.setLayout(new BoxLayout(jpanButtons, BoxLayout.X_AXIS));
        jpanButtons.add(Box.createHorizontalGlue());
        jpanButtons.add(btnStoreData);
        jpanButtons.add(Box.createHorizontalStrut(10));
        jpanButtons.add(btnExit);
        jpanButtons.add(Box.createHorizontalStrut(15));

        return jpanButtons;
    }

    private void storeDataTriggered() {
        // Get GUI data.
        Object temp = cmbInstrument.getSelectedItem();
        if(temp == null) {
            JOptionPane.showMessageDialog(this, "Please select an instrument.", "No instrument selected!", JOptionPane.WARNING_MESSAGE);
            cmbInstrument.requestFocus();
            return;
        }
        Instrument instrument = (Instrument)temp;
        temp = cmbExperiment.getSelectedItem();
        if(temp == null) {
            JOptionPane.showMessageDialog(this, "Please select an experiment.", "No experiment selected!", JOptionPane.WARNING_MESSAGE);
            cmbExperiment.requestFocus();
            return;
        }
        Experiment experiment = (Experiment)temp;

        String inputFolderString = txtInputFolder.getText();
        if(inputFolderString == null || inputFolderString.trim().equals("")) {
            JOptionPane.showMessageDialog(this, "You need to select the top-level input folder.", "No input folder selected!", JOptionPane.WARNING_MESSAGE);
            txtInputFolder.requestFocus();
            return;
        }

        File inputFolder = new File(inputFolderString);
        if(!inputFolder.exists()) {
            JOptionPane.showMessageDialog(this, "The top-level input folder you specified does not exist!", "Input folder does not exist!", JOptionPane.WARNING_MESSAGE);
            txtInputFolder.requestFocus();
            return;
        }
        if(!inputFolder.isDirectory()) {
            JOptionPane.showMessageDialog(this, "The top-level input folder you specified is a file, not a folder!", "Invalid input folder selected!", JOptionPane.WARNING_MESSAGE);
            txtInputFolder.requestFocus();
            return;
        }

        // OK, now the real work begins.
        // First we need to list the subfolders and input files we'll need.
        DefaultProgressBar progress = new DefaultProgressBar(this, "Storing LC runs in the database", 0, 100);
        progress.setSize((int)(this.getWidth()/1.2), progress.getPreferredSize().height);
        progress.setIndeterminate(true);

        TagSummaryFileFinderWorker tsfw = new TagSummaryFileFinderWorker(inputFolder, progress);
        tsfw.start();
        progress.setVisible(true);

        File[] inputFiles = (File[])tsfw.get();

        int maximum = (inputFiles.length*2) + 2;
        progress = new DefaultProgressBar(this, "Storing LC runs in the database", 0, maximum);
        progress.setSize((int)(this.getWidth()/1.2), progress.getPreferredSize().height);

        // Now delegate the real work to the storage worker.
        StoreDataWorker sdw = new StoreDataWorker(iConn, experiment, inputFiles, instrument, this, progress);
        sdw.start();
        progress.setVisible(true);

        // All done, signal user!
        JOptionPane.showMessageDialog(this, "All data has been stored!", "Data storage complete", JOptionPane.INFORMATION_MESSAGE);
    }
}
